# 函数定义与调用
'''
函数：是指一段可以直接被另一段程序或代码引用的沉痼或代码。
在编写代码时，常将一些常用的功能模块编写成函数，放在函数库中供公共使用，可以减少重复编写程序端和简化diamagnetic结构。

语法：
def 函数名称(参数1，参数2，……):
    <代码块>
    return <表达式>

示例：
def hello():
    print("Hello World!")
hello()  #调用函数
'''

#h = "hello wprld"
#print(h)

# 函数传参
'''
def hello(a,b):
    print(f"这是函数内引用的变量{a}")
    print(a)
    print(b)
    return a+b

print(hello(6,3))    #传参
'''


# 接收任意数量参数
'''
前面学习的是固定参数数量，当不知道有多少个参数时可以在参数前面加*与**，表示可接收任意多个参数。
*name 接收任意多个参数，并放到一个元组中。
**name 接收一个键值，并存储为字典。
'''

# 示例：传入多个参数，计算总和
'''
def func(*seq):
    x = 0
    for n in seq:
        x += n
    return x
print(func(1,2,3))
'''

# 示例： 传入多个键值，进行处理
'''
def func(**computer):
    for k,v in computer.items():
        print("名称: %s\t价格:%s" %(k,v))
func(主机=5000,显示器=1000,鼠标=60,键盘=150)
'''

#print('----------------------')
'''
def func(*args, **kwargs):
    print(args)
    print(kwargs)

func(1,2,3,a=300,b=50,c=10)
'''

# 匿名函数：没有名字的函数，使用lambda关键字定义，一般仅用于单个表达式。
'''
示例：求和函数
s = lambda a, b: a+b
print(s(1,2))
等价于
def func(a,b):
    :return a+b
'''

#s = lambda a,b: a+b
#print(s(33,33)

#t = lambda x: "这是一个偶数" if x % 2 == 0 else "这是一个奇数"
#print(t(81))


# 作用域：限制一个变量或一段代码可用范围。好处是提高程序逻辑的局部性，减少名字冲突。
'''
Local(局部)->Enclosed(嵌套)->Global(全局)->Built-in(内置)

1.Local(局部作用域): 在函数中定义的变量。
2.Enclosed(嵌套作用域): 一般出现在函数中嵌套一个函数时，在外部函数中的作用域称为嵌套作用域(闭包常见)。
3.Global(全局作用域): 文件顶层定义的变量。
4.Built-in(内置作用域): 系统内解释器定义的变量，例如关键字。  
'''

# 全局作用域
'''
a = 2
def func():
    global b
    b = 3
func()
print(a)
print(b)
'''

# 内置作用域
import builtins
dir(dir(builtins))

'''
x = 123           # 局部作用域，全局变量
def outer():
    x = 456              # 嵌套作用域， 外部变量
    def inner():
        print(x)
    return inner()
outer()
'''


# 闭包: 你可以简单粗暴的理解为：闭包就是一个定义在函数内部的函数，闭包使得变量即使脱离了该函数的作用域范围也依然能被访问。像上面所说的嵌套也是闭包的一种形式（将内部嵌套定义的函数作为返回值）。
'''
示例：
def outer():
    x = 1
    def inner():
        print(x)   #1
    return inner   #不加括号，表示返回函数体
sf = outer()
sf()  #调用，等同于outer()()

在一个外部函数内定义了一个函数，内部函数引用外部函数的变量，并且外部函数的返回值是内部的引用，这样就构成了一个闭包，并且满足了闭包的三个条件：
1.两层以上嵌套关系
2.内部函数调用外部定义的变量
3.外部函数返回内部函数体对象，而不是函数体结果（加括号）
'''

def outer():
    x = 1
    def inner():
        print(x)   #1
    return inner   #不加括号，表示返回函数体
sf = outer()
#sf()    #调用，等同于outer()()
outer()()


# 函数装饰器：装饰器本质上是一个函数，它可以让其它函数在不需要任何代码变动的前提下增加额外功能，装饰器的返回值也是一个函数对象。
# 应用场景：记得日志、性能测试、权限效验等

'''
# 示例： 给一个函数增加一个记录日志的功能
def hello():
    print("我是原函数")
def hello2():
    print("我是一个求和的函数，正在执行……")

#hello()
# 定义
def decorator(func):
    def f():
        print("记录一下开始。")
        func()
        print("记录一下结束。")
    return f
# 调用
dec = decorator(hello)
dec()
#decorator(hello)()
decorator(hello2)()
'''

# Python提供一个更简洁引用装饰器的方法：语法糖 "@"
'''
示例:
@decoorator
def hello():
    print("我是原函数")

hello()
'''

# 示例： 给一个函数增加一个记录日志的功能 (无参数)
'''
# 定义
def decorator(func):
    def f():
        print("记录一下开始。")
        func()
        print("记录一下结束。")
    return f      #返回函数体
def hello():
    print("我是原函数")
#hello()

@decorator       #调用内部函数  等同于 decorator(hello2)()
def hello2():
    print("我是一个求和的函数，正在执行……")
hello2()

# 调用
dec = decorator(hello)    #装饰器传入函数
dec()    #调用内部函数  等同于 decorator(hello)()
'''

# 函数装饰器: (带参数)
'''
示例:
def decorator(func):
    def f(msg):
    print("原函数开始执行了")
    func(msg)
    print("原函数执行结束了")
return f

@decorator
def hello(msg):
    print(msg)

hello("我是原函数")
'''

def decorator(func):       #函数装饰器
    def f(*age,**name):    #内部带参数的函数，*和**的区别(*age 接收任意多个参数，并放到一个元组中。**name 接收一个键值，并存储为字典。)
        print("记录一下开始。")
        func(*age,**name)
        print("记录一下结束。")
    return f      #返回函数体

@decorator       #调用内部函数  等同于 decorator(hello2)()
def hello2(*age,**name):    #装饰器传入函数
    print(age,name)
    print("我是一个求和的函数，正在执行……")
hello2(4,5,6,a=1,b=2,c=3)             #给内部函数传参